<?php

namespace App\Console\Commands\Poi;

use App\Http\Middleware\OpenShop\WangGuiCityByPower;
use App\Models\Common\CityTerritorialBoundaryModel;
use App\Models\Wanggui\BusinessAoiModel;
use App\Service\Common\CoordinateService;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

class BusinessPoi extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'poi:get-business-poi';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '获得全国商圈数据';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    public string $dataSource = '';
    
    /**
     * Execute the console command.
     * @throws GuzzleException
     */
    public function handle()
    {
        ini_set('memory_limit', '512M');

        $this->info('----------二手房商圈获取---------');
        $this->dataSource = 'ESF';
        $this->doHandle();
        
        $this->info('----------租房商圈获取---------');
        $this->dataSource = 'ZF';
        $this->doHandle();

        $this->updateLatAndLng();
    }

    /**
     * @return void
     */
    public function doHandle()
    {
        //获得所有城市列表
        $areaList = Cache::remember('poi:business:areaList', 86400 * 7, function () {
            return self::getAreaList();
        });

        //获得所有城市的最大最小经纬度
        $areaLatLng = Cache::remember('poi:business:areaBoundary', 86400 * 120, function () {
            return CityTerritorialBoundaryModel::query()->whereIn('level', ['city', 'province'])->select(['name', 'adcode', 'boundary'])->get()->toArray();
        });

        $areaObject = collect($areaLatLng);
        foreach ($areaList as $area) {
            //循环所有的省份  循环结果分为一级二级
            foreach ($area['list'] as $value) {
                if ($value['name'] == '待定中') {
                    continue;
                }
                $boundaryRes = [];
                $boundary    = $areaObject->filter(function ($area) use ($value) {
                    return mb_strpos($area['name'], $value['name']) !== false;
                })->values();
                if ($boundary->count() != 1) {
                    //$this->info($value['name'] . '结果集包含' . $boundary->count() . '结果集');
                    $areaBoundary = collect($boundary)->filter(function ($area) {
                        return mb_strpos($area['name'], '市') !== false;
                    })->values();
                    if ($areaBoundary->count() == 1) {
                        $boundaryRes = $areaBoundary->toArray()[0];
                    }
                } else {
                    $boundaryRes = $boundary->toArray()[0];
                }
                if (!isset($boundaryRes['boundary'])) continue;
                $districtList = $this->getCityBusinessList($value['id'], 'district', json_decode($boundaryRes['boundary'], true))['bubbleList'] ?? [];

                //todo 区级入库
                $tempDistrictData = [];
                foreach ($districtList as $district) {
                    $border = collect(explode(';', $district['border']))->map(function ($item) {
                        $arr = explode(',', $item);
                        return [
                            'lng' => $arr[0],
                            'lat' => $arr[1]
                        ];
                    });
                    try {
                        $administration = self::getAdministrationByCode($district['longitude'], $district['latitude']);
                        if ($administration['city'] == $administration['district']) {
                            $administration['city'] = $administration['province'];
                        }
                    } catch (\Exception $exception) {
                        continue;
                    }
                    $model = BusinessAoiModel::query()
                        ->firstOrNew([
                            'name'     => $district['name'],
                            'province' => $administration['province'],
                            'city'     => $administration['city'],
                            'district' => $administration['district'],
                            'cityCode' => $value['id'],
                            'level'    => 'district',
                        ]);

                    $model->lng = $district['longitude'];
                    $model->lat = $district['latitude'];
                    $model->boundary = json_encode($border);
                    $model->save();
                }
                //BusinessAoiModel::query()->insert($tempDistrictData);
                $this->info($value['name'] . 'district级别的商圈添加完成');
                //通过区级拿到商圈
                $businessList = $this->getCityBusinessList($value['id'], 'bizcircle', json_decode($boundaryRes['boundary'], true))['bubbleList'] ?? [];

                if (count($businessList) >= 99) {
                    $businessList = [];
                    $businessListEqually = [];
                    $tempBoundary = json_decode($boundaryRes['boundary'], true);
                    // 分成4分查询

                    $equally = self::equallyDivideBoundary($tempBoundary);
                    foreach ($equally as $itemEqually) {
                        $businessListEqually = $this->getCityBusinessList($value['id'], 'bizcircle', $itemEqually)['bubbleList'] ?? [];
                        dump(count($businessListEqually));

                        if (count($businessListEqually) >= 99) {
                            $businessListEqually1 = [];
                            // 分成4分查询
                            $equally = self::equallyDivideBoundary($itemEqually);
                            foreach ($equally as $itemEqually1) {
                                $businessListEqually1 = $this->getCityBusinessList($value['id'], 'bizcircle', $itemEqually1)['bubbleList'] ?? [];
                                dump('下一级' . count($businessListEqually1));
                                if (count($businessListEqually1) >= 99) {
                                    $businessListEqually2 = [];
                                    // 分成4分查询
                                    $equally = self::equallyDivideBoundary($itemEqually1);
                                    foreach ($equally as $itemEqually2) {
                                        $businessListEqually2 = $this->getCityBusinessList($value['id'], 'bizcircle', $itemEqually2)['bubbleList'] ?? [];
                                        dump('下下一级' . count($businessListEqually2));
                                        if (count($businessListEqually2) >= 99) {
                                            $businessListEqually3 = [];
                                            // 分成4分查询
                                            $equally = self::equallyDivideBoundary($itemEqually2);
                                            foreach ($equally as $itemEqually3) {
                                                $businessListEqually3 = $this->getCityBusinessList($value['id'], 'bizcircle', $itemEqually3)['bubbleList'] ?? [];
                                                dump('下下下一级' . count($businessListEqually3));
                                                $businessList = array_merge($businessList, $businessListEqually3);
                                            }
                                        } else {
                                            $businessList = array_merge($businessList, $businessListEqually2);
                                        }
                                    }
                                } else {
                                    $businessList = array_merge($businessList, $businessListEqually1);
                                }
                            }
                        } else {
                            $businessList = array_merge($businessList, $businessListEqually);
                        }
                    }
                }

                //todo 商圈入库
                $tempBusinessData = [];
                foreach ($businessList as $district) {
                    $border = collect(explode(';', $district['border']))->map(function ($item) {
                        $arr = explode(',', $item);
                        return [
                            'lng' => $arr[0],
                            'lat' => $arr[1]
                        ];
                    });
                    try {
                        $administration = self::getAdministrationByCode($district['longitude'], $district['latitude']);
                        if ($administration['city'] == $administration['district']) {
                            $administration['city'] = $administration['province'];
                        }
                    } catch (\Exception $exception) {
                        continue;
                    }
                    $model = BusinessAoiModel::query()
                        ->firstOrNew([
                            'name'     => $district['name'],
                            'province' => $administration['province'],
                            'city'     => $administration['city'],
                            'district' => $administration['district'],
                            'cityCode' => $value['id'],
                            'level'    => 'bizcircle',
                        ]);

                    $model->lng = $district['longitude'];
                    $model->lat = $district['latitude'];
                    $model->boundary = json_encode($border);
                    $model->save();
                }
                //BusinessAoiModel::query()->insert($tempBusinessData);
                $this->info($value['name'] . 'bizcircle级别的商圈添加完成');
            }
        }
    }


    /**
     * 获得城市列表
     * @return array
     * @throws GuzzleException
     */
    public static function getAreaList(): array
    {
        $url      = "https://map.ke.com/proxyApi/i.c-pc-webapi.ke.com/map/initdata?cityId=310000&dataSource=ESF";
        $response = (new Client(['http_errors' => false]))->get($url);
        $body     = $response->getBody()->getContents();
        $areaArr  = json_decode($body, true)['data']['cityList'];
        $areaList = [];
        foreach ($areaArr as $one) {
            foreach ($one['list'] as $two) {
                $areaList[] = [
                    'name' => $two['name'],
                    'list' => $two['list']
                ];
            }
        }
        return $areaList;
    }


    /**
     * 获得城市下的商圈列表
     * @param int $cityCode
     * @param string $groupType
     * @param array $boundary
     * @return array
     */
    public function getCityBusinessList(int $cityCode, string $groupType, array $boundary): array
    {
        return Http::withOptions([
            'http_errors' => false,
        ])->withHeaders([
            'accept' => 'application/json, text/plain, */*',
        ])->get(
            'https://map.ke.com/proxyApi/i.c-pc-webapi.ke.com/map/bubblelist', array_merge([
                'cityId'     => $cityCode,
                'groupType'  => $groupType,
                'dataSource' => $this->dataSource,
            ], $boundary),
        )->json('data');
    }

    /**
     * 通过经纬度获得省市区
     * @param float $lng
     * @param float $lat
     * @return array
     */
    public static function getAdministrationByCode(float $lng, float $lat): array
    {
        $amapKey = WangGuiCityByPower::getRandomAmapKey();
        return Cache::rememberForever('poi:business:location:' . $lng . ',' . $lat, function () use ($lng, $lat, $amapKey) {
            $body = Http::withOptions([
                'http_errors' => false,
            ])->get(
                'https://restapi.amap.com/v3/geocode/regeo', [
                    'output'     => 'json',
                    'location'   => $lng . ',' . $lat,
                    'extensions' => 'base',
                    'batch'      => 'false',
                    'roadlevel'  => 1,
                    'key'        => $amapKey,
                ],
            )->json();

            if (!isset($body['status']) || $body['status'] != 1) {
                return [
                    'province' => '',
                    'city'     => '',
                    'district' => '',
                ];
            }
            $province = empty($body['regeocode']['addressComponent']['province']) ? '' : $body['regeocode']['addressComponent']['province'];
            $city     = empty($body['regeocode']['addressComponent']['city']) ? '' : $body['regeocode']['addressComponent']['city'];
            $district = empty($body['regeocode']['addressComponent']['district']) ? '' : $body['regeocode']['addressComponent']['district'];
            $township = empty($body['regeocode']['addressComponent']['township']) ? '' : $body['regeocode']['addressComponent']['township'];

            return [
                'province' => $province,
                'city'     => $city ?: $district,
                'district' => $district ?: $township,
            ];
        });
    }

    /**
     * 对于大面积来说要分块处理
     * @param $boundary
     * @return array[]
     */
    private static function equallyDivideBoundary($boundary): array
    {
        $lat = ($boundary['maxLatitude'] + $boundary['minLatitude']) / 2;
        $lng = ($boundary['maxLongitude'] + $boundary['minLongitude']) / 2;

        return [
            [
                "maxLatitude"  => $boundary['maxLatitude'],
                "minLatitude"  => $lat,
                "maxLongitude" => $boundary['maxLongitude'],
                "minLongitude" => $lng,
            ], [
                "maxLatitude"  => $boundary['maxLatitude'],
                "minLatitude"  => $lat,
                "maxLongitude" => $lng,
                "minLongitude" => $boundary['minLongitude'],
            ], [
                "maxLatitude"  => $lat,
                "minLatitude"  => $boundary['minLatitude'],
                "maxLongitude" => $boundary['maxLongitude'],
                "minLongitude" => $lng,
            ], [
                "maxLatitude"  => $lat,
                "minLatitude"  => $boundary['minLatitude'],
                "maxLongitude" => $lng,
                "minLongitude" => $boundary['minLongitude'],
            ]
        ];
    }


    /**
     * 百度坐标系转火星坐标系
     * @return void
     */
    public function updateLatAndLng()
    {
        BusinessAoiModel::query()
            ->chunkById(100, function ($aois) {
                foreach ($aois as $aoi) {
                    $newCoordinate = CoordinateService::bd09ToGcj02($aoi->lng, $aoi->lat);
                    try {
                        $boundary = collect(json_decode($aoi->boundary, true))->map(function ($item) {
                            $tmp = CoordinateService::bd09ToGcj02($item['lng'], $item['lat']);
                            return [
                                'lat' => $tmp['lat'],
                                'lng' => $tmp['lon'],
                            ];
                        })->toArray();
                    } catch (\Exception $exception) {
                        continue;
                    }
                    $aoi->lat = $newCoordinate['lat'];
                    $aoi->lng = $newCoordinate['lon'];
                    $aoi->boundary = json_encode($boundary);
                    $aoi->save();
                    $this->info($aoi->id);
                }
            });
    }

}
